package longshu.data.config;

import lombok.extern.slf4j.Slf4j;
import longshu.data.common.context.SpringContext;
import longshu.data.common.dao.BaseDao;
import longshu.data.common.dao.DaoMarker;
import longshu.data.common.ds.DataSourceKey;
import longshu.data.common.ds.DynamicDataSource;
import longshu.data.common.mybatis.DynamicSqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import tk.mybatis.mapper.mapperhelper.MapperHelper;

/**
 * MyBatisConfig
 *
 * @author LongShu 2018/01/19
 */
@Slf4j
@Configuration
@ConditionalOnBean(DataSourceConfig.class)
@MapperScan(value = "longshu.data.dao", markerInterface = DaoMarker.class)
public class MyBatisConfig {
    private static final String MYBATIS_CONFIG = "classpath:mybatis-config.xml";

    private static final String MYSQL_MAPPER = "classpath:mapper/mysql/*.xml";
    private static final String H2_MAPPER = MYSQL_MAPPER;// H2基本兼容MySQL
    private static final String SQLITE_MAPPER = "classpath:mapper/sqlite/*.xml";
    private static final String HSQLDB_MAPPER = "classpath:mapper/hsqldb/*.xml";

    @Autowired
    private DynamicDataSource dataSource;

    /**
     * SqlSessionFactory
     */
    @Primary
    @Bean("sqlSessionFactory")
    public DynamicSqlSessionFactory sqlSessionFactory() {
        DynamicSqlSessionFactory factory = DynamicSqlSessionFactory.getInstance();

        factory.addSqlSessionFactory(DataSourceKey.MYSQL, mysqlSqlSessionFactory());
        factory.addSqlSessionFactory(DataSourceKey.H2, h2SqlSessionFactory());
        factory.addSqlSessionFactory(DataSourceKey.SQLITE, sqliteSqlSessionFactory());
        factory.addSqlSessionFactory(DataSourceKey.HSQLDB, hsqldbSqlSessionFactory());

        // 和 DynamicDataSource 一致
        factory.setDefaultSqlSessionFactory(sqliteSqlSessionFactory());
        return factory;
    }

    /**
     * 不同数据库使用不同 mapper.xml
     */
    @Bean("mysqlSqlSessionFactory")
    public SqlSessionFactory mysqlSqlSessionFactory() {
        SqlSessionFactory sessionFactory = createSqlSessionFactoryBean(MYSQL_MAPPER);
        return registerMapperHelper(sessionFactory);
    }

    @Bean("h2SqlSessionFactory")
    public SqlSessionFactory h2SqlSessionFactory() {
        SqlSessionFactory sessionFactory = createSqlSessionFactoryBean(H2_MAPPER);
        return registerMapperHelper(sessionFactory);
    }

    @Bean("sqliteSqlSessionFactory")
    public SqlSessionFactory sqliteSqlSessionFactory() {
        SqlSessionFactory sessionFactory = createSqlSessionFactoryBean(SQLITE_MAPPER);
        return registerMapperHelper(sessionFactory);
    }

    @Bean("hsqldbSqlSessionFactory")
    public SqlSessionFactory hsqldbSqlSessionFactory() {
        SqlSessionFactory sessionFactory = createSqlSessionFactoryBean(HSQLDB_MAPPER);
        return registerMapperHelper(sessionFactory);
    }

    SqlSessionFactory createSqlSessionFactoryBean(String locationPattern) {
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        // 使用 DynamicDataSource 动态数据源
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setVfs(SpringBootVFS.class);
        Resource configLocation = SpringContext.getContext().getResource(MYBATIS_CONFIG);
        sessionFactoryBean.setConfigLocation(configLocation);

        try {
            Resource[] mapperLocations = SpringContext.getContext().getResources(locationPattern);
            sessionFactoryBean.setMapperLocations(mapperLocations);
            return sessionFactoryBean.getObject();
        } catch (Exception e) {
            throw new BeanInitializationException(e.getMessage(), e);
        }
    }

    /**
     * 注册通用 Mapper
     */
    SqlSessionFactory registerMapperHelper(SqlSessionFactory sessionFactory) {
        MapperHelper mapperHelper = new MapperHelper();
        mapperHelper.registerMapper(BaseDao.class);
        mapperHelper.processConfiguration(sessionFactory.getConfiguration());
        return sessionFactory;
    }

    /**
     * SqlSessionTemplate
     */
    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}
